#include "types.h"
#include "globals.h"
#include "errno.h"

#include "util/debug.h"
#include "util/string.h"

#include "proc/proc.h"
#include "proc/kthread.h"

#include "mm/mm.h"
#include "mm/mman.h"
#include "mm/page.h"
#include "mm/pframe.h"
#include "mm/mmobj.h"
#include "mm/pagetable.h"
#include "mm/tlb.h"

#include "fs/file.h"
#include "fs/vnode.h"

#include "vm/shadow.h"
#include "vm/vmmap.h"

#include "api/exec.h"

#include "main/interrupt.h"


/* Pushes the appropriate things onto the kernel stack of a newly forked thread
 * so that it can begin execution in userland_entry.
 * regs: registers the new thread should have on execution
 * kstack: location of the new thread's kernel stack
 * Returns the new stack pointer on success. */
static uint32_t
fork_setup_stack(const regs_t *regs, void *kstack)
{
        /* Pointer argument and dummy return address, and userland dummy return
         * address */
        uint32_t esp = ((uint32_t) kstack) + DEFAULT_STACK_SIZE - (sizeof(regs_t) + 12);
        *(void **)(esp + 4) = (void *)(esp + 8); /* Set the argument to point to location of struct on stack */
        memcpy((void *)(esp + 8), regs, sizeof(regs_t)); /* Copy over struct */
        return esp;
}


/*
 * The implementation of fork(2). Once this works,
 * you're practically home free. This is what the
 * entirety of Weenix has been leading up to.
 * Go forth and conquer.
 */
int
do_fork(struct regs *regs)
{
/*        NOT_YET_IMPLEMENTED("VM: do_fork");*/

           KASSERT(regs != NULL);
           KASSERT(curproc != NULL);
           KASSERT(curproc->p_state == PROC_RUNNING);

		   proc_t *newproc = proc_create("fork_proc");
		   newproc->p_pproc = curproc;

        newproc->p_brk = curproc->p_brk;
        newproc->p_start_brk = curproc->p_start_brk;
      /*   newproc->p_vmmap = vmmap_create();
         newproc->p_vmmap->vmm_proc = newProcess;*/


		   /* Allocates a new vmmap containing a new vmarea for each area in the
 * given map. The areas should have no mmobjs set yet. Returns pointer
 * to the new vmmap on success, NULL on failure. This function is
 * called when implementing fork(2). */
		   newproc->p_vmmap = vmmap_clone(curproc->p_vmmap);  /*after clone still no objects*/
       newproc->p_vmmap->vmm_proc = newproc;


          /*  showVmMapInfo(newproc->p_vmmap, "new process\n");
            showVmMapInfo(curproc->p_vmmap, "parent process\n");*/

     list_link_t *link, *link1;
     for (link = curproc->p_vmmap->vmm_list.l_next, link1 = newproc->p_vmmap->vmm_list.l_next;
          link != &curproc->p_vmmap->vmm_list && link1 != &newproc->p_vmmap->vmm_list;
           link = link->l_next, link1 = link1->l_next)
     {
        	vmarea_t* curproc_vma = list_item(link, vmarea_t, vma_plink);
        	vmarea_t* newproc_vma = list_item(link1, vmarea_t, vma_plink);

          newproc_vma->vma_obj = curproc_vma->vma_obj;
          newproc_vma->vma_obj->mmo_ops->ref(curproc_vma->vma_obj);
          continue;


        		if(curproc_vma->vma_flags & MAP_SHARED){
	        			/*second part also point to this object, add reference*/
        			newproc_vma->vma_obj = curproc_vma->vma_obj;
        			newproc_vma->vma_obj->mmo_ops->ref(curproc_vma->vma_obj);
              KASSERT(NULL && "have not add the vma into the object's list");

	        	}else{
	        		    /*assign a new shadow object for each*/        			
						mmobj_t* newObj1 = shadow_create();
						/*newObj1 = curproc_vma->vma_obj;               KASSERT(NULL && "newOBJ1 then missing?");*/
            newObj1->mmo_shadowed = curproc_vma->vma_obj;
            newObj1->mmo_un.mmo_bottom_obj = mmobj_bottom_obj(curproc_vma->vma_obj);
						curproc_vma->vma_obj->mmo_ops->ref(curproc_vma->vma_obj);

						mmobj_t* newObj2 = shadow_create();
					/*	newObj2 = curproc_vma->vma_obj;*/
            newObj2->mmo_shadowed = curproc_vma->vma_obj;
            newObj2->mmo_un.mmo_bottom_obj = mmobj_bottom_obj(curproc_vma->vma_obj);
						curproc_vma->vma_obj->mmo_ops->ref(curproc_vma->vma_obj);

            curproc_vma->vma_obj->mmo_ops->put(curproc_vma->vma_obj);

						curproc_vma->vma_obj = newObj1;
						/*newObj1->mmo_ops->ref(newObj1);*/
						newproc_vma->vma_obj = newObj2;
					/*	newObj2->mmo_ops->ref(newObj2);*/

           /* pt_unmap_range(curproc->p_pagedir, PN_TO_ADDR(newproc_vma->vma_start), PN_TO_ADDR(newproc_vma->vma_end));
            */
						/*second part also point to this object*/
	        	}
     }
     /*showVmMapInfo(newproc->p_vmmap, "new process vmmap after give values\n");*/


     /*call kthread clone here*/
     	kthread_t* newthr = kthread_clone(curthr);
        
      /*link the thread and process*/
      newthr->kt_proc = newproc;
      list_insert_tail(&newproc->p_threads, &newthr->kt_plink);

     /* context_setup(&newthr->kt_ctx, userland_entry, 0, 0, newthr->kt_kstack, DEFAULT_STACK_SIZE, newproc->p_pagedir);*/

      KASSERT(newthr->kt_kstack);
  		newthr->kt_ctx.c_kstack = (uintptr_t)newthr->kt_kstack;
      newthr->kt_ctx.c_kstacksz = DEFAULT_STACK_SIZE;

    
      KASSERT(newproc->p_pagedir != NULL);
      
   /* pt_unmap_range(curproc->p_pagedir, USER_MEM_LOW, USER_MEM_HIGH);*/
    tlb_flush_all();

    /*   memcpy(newproc->p_pagedir, curproc->p_pagedir, PAGE_SIZE * 2);*/

       newthr->kt_ctx.c_eip = userland_entry;
     newthr->kt_ctx.c_pdptr = newproc->p_pagedir;

     regs->r_eax = 0;

     newthr->kt_ctx.c_esp = fork_setup_stack(regs,  newthr->kt_kstack);
     /*newthr->kt_ctx.c_ebp = newthr->kt_ctx.c_esp;*/
     

        sched_make_runnable(newthr);
        newproc->p_state = PROC_RUNNING;
        KASSERT(newproc->p_state == PROC_RUNNING);
        KASSERT(newproc->p_pagedir != NULL);
        KASSERT(newthr->kt_kstack != NULL);

        showVmMapInfo(curproc->p_vmmap, "curproc\n");
        showVmMapInfo(newproc->p_vmmap, "newproc\n");
      /*  tlb_flush_all();*/
    return newproc->p_pid;
}
